import os
import json
import subprocess
import time

import win32print
from flask import Flask, request, jsonify
from flask_cors import CORS
from threading import Thread
from pystray import Icon, Menu, MenuItem
from PIL import Image
from waitress import serve
import tkinter as tk
from tkinter import filedialog, messagebox
import sys
import shlex
import msvcrt

# 强制设置工作目录为当前脚本所在目录
BASE_DIR = os.path.dirname(os.path.abspath(sys.argv[0]))
os.chdir(BASE_DIR)

# 配置文件路径
CONFIG_FILE = os.path.join(BASE_DIR, "config.json")

# 固定 SumatraPDF 的路径
SUMATRAPDF_PATH = os.path.join(BASE_DIR, "SumatraPDF-3.5.2-64.exe")

# 定义互斥锁文件
MUTEX_FILE = os.path.join(BASE_DIR, "app.lock")

# 加载配置文件
def load_config():
    if not os.path.exists(CONFIG_FILE):
        raise RuntimeError(f"配置文件 {CONFIG_FILE} 不存在，请确保配置文件在当前目录中！")
    with open(CONFIG_FILE, "r", encoding="utf-8") as f:
        return json.load(f)

CONFIG = load_config()

CHROME_PATH = CONFIG["CHROME_PATH"]
TEMP_DIR = CONFIG["TEMP_DIR"]
FLASK_HOST = CONFIG["FLASK_HOST"]
FLASK_PORT = CONFIG["FLASK_PORT"]

# 创建 Flask 应用
app = Flask(__name__)

@app.before_request
def handle_options():
    if request.method == 'OPTIONS':
        response = jsonify({'message': 'Preflight request'})
        response.headers['Access-Control-Allow-Origin'] = '*'
        response.headers['Access-Control-Allow-Methods'] = 'GET, POST, OPTIONS'
        response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
        return response

app.config['JSON_AS_ASCII'] = False
CORS(app)  # 启用跨域支持

def get_default_printer():
    try:
        return win32print.GetDefaultPrinter()
    except Exception as e:
        raise RuntimeError(f"无法获取默认打印机: {e}")


def query_printer_status(printer_name):
    """
    查询指定打印机的最新任务状态，包含已完成的任务
    """
    try:
        printer_handle = win32print.OpenPrinter(printer_name)
        last_job_id = None  # 用于保存最后一次查询的 JobId

        start_time = time.time()
        timeout = 30  # 超时时间设定为 30 秒

        while True:
            printer_jobs = win32print.EnumJobs(printer_handle, 0, -1, 1)

            if printer_jobs:
                # 获取最新的打印任务（按提交时间排序）
                latest_job = max(printer_jobs, key=lambda x: x['Submitted'])
                last_job_id = latest_job["JobId"]  # 更新 JobId

                status = latest_job["Status"]

                if status & win32print.JOB_STATUS_PRINTING:
                    print("任务正在打印...")
                elif status & win32print.JOB_STATUS_PRINTED:
                    return "success", "打印成功"
                elif status & win32print.JOB_STATUS_ERROR:
                    return "error", "打印错误"
                elif status & win32print.JOB_STATUS_PAUSED:
                    return "error", "打印暂停"
                elif status & win32print.JOB_STATUS_DELETING:
                    return "error", "打印任务被删除"
                elif status & win32print.JOB_STATUS_BLOCKED_DEVQ:
                    return "error", "打印机被阻止"
                elif status & win32print.JOB_STATUS_OFFLINE:
                    return "error", "打印机离线"
                elif status & win32print.JOB_STATUS_PAPEROUT:
                    return "error", "打印机缺纸"
                elif status & win32print.JOB_STATUS_USER_INTERVENTION:
                    return "error", "需要用户干预"
                elif status & win32print.JOB_STATUS_RESTART:
                    return "error", "打印任务重启"
                else:
                    print("未知状态，继续监控...")

            elif last_job_id is not None:
                # 如果队列中没有任务，但我们知道最后一个任务的 ID，则推测任务已完成
                return "success", "打印成功"

            # 检查是否超时
            if time.time() - start_time > timeout:
                return "error", "打印超时"

            # 检查间隔，避免占用过多资源
            time.sleep(1)
    except Exception as e:
        return "error", f"查询打印机状态失败: {e}"
    finally:
        win32print.ClosePrinter(printer_handle)

@app.route('/status', methods=['GET'])
def check_status():
    return jsonify({
        "status": "running",
        "host": FLASK_HOST,
        "port": FLASK_PORT
    })


@app.route('/print', methods=['POST'])
def print_html():
    try:
        data = request.json
        html_content = data.get("html", "")
        user_printer_name = data.get("printer_name")
        printer_name = user_printer_name if user_printer_name else get_default_printer()
        pdf_path = os.path.join(TEMP_DIR, "temp.pdf")
        html_path = os.path.join(TEMP_DIR, "temp.html")

        if not html_content:
            return jsonify({"status": "error", "message": "HTML 内容为空"}), 400

        # 保存 HTML 内容到临时文件
        with open(html_path, "w", encoding="utf-8") as tmp_file:
            tmp_file.write(html_content)

        # 使用 Chrome 生成 PDF
        generate_pdf_with_chrome(html_path, pdf_path)

        # 使用 SumatraPDF 静默打印 PDF
        status, message = silent_print_with_sumatra(pdf_path, printer_name)

        # 查询打印状态
        if status == "success":
            status, message = query_printer_status(printer_name)

        return jsonify({"status": status, "message": message})

    except Exception as e:
        return jsonify({"status": "error", "message": str(e)}), 500
    finally:
        for file in [html_path, pdf_path]:
            if os.path.exists(file):
                os.remove(file)


def generate_pdf_with_chrome(html_path, output_pdf_path):
    """
    使用 Google Chrome 生成 PDF 文件
    """
    try:
        # 确保路径包含空格时，正确包装路径
        command = [
            f'"{CHROME_PATH}"',
            "--headless",
            "--disable-gpu",
            f"--print-to-pdf=\"{output_pdf_path}\"",
            f"\"{html_path}\""
        ]

        # 使用 shell=True 确保完整命令被正确解析
        subprocess.run(" ".join(command), shell=True, check=True)
    except subprocess.CalledProcessError as e:
        raise RuntimeError(f"Chrome 生成 PDF 失败: {e}")


def silent_print_with_sumatra(pdf_path, printer_name):
    """
    使用 SumatraPDF 静默打印 PDF 文件
    """
    try:
        # 确保路径和参数正确处理
        sumatra_command = [
            SUMATRAPDF_PATH,
            "-print-to", printer_name,
            "-print-settings", "",
            pdf_path
        ]

        # 运行命令
        subprocess.run(sumatra_command, check=True, shell=False)
        return "success", "打印成功"

    except subprocess.CalledProcessError as e:
        return "error", f"SumatraPDF 打印失败: {e}"
    except FileNotFoundError as e:
        return "error", f"SumatraPDF 文件路径无效: {e}"
    except Exception as e:
        return "error", f"打印时发生未知错误: {e}"


def open_config_window(icon):
    def save_and_restart():
        new_config = {
            "CHROME_PATH": chrome_path.get(),
            "TEMP_DIR": temp_dir.get(),
            "FLASK_HOST": host.get(),
            "FLASK_PORT": int(port.get())
        }
        with open(CONFIG_FILE, "w", encoding="utf-8") as f:
            json.dump(new_config, f, indent=4)
        messagebox.showinfo("提示", "配置已保存，程序将重启！")
        os.execv(sys.executable, [sys.executable] + sys.argv)

    def on_close():
        root.destroy()

    root = tk.Tk()
    root.title("配置参数")
    root.geometry("800x300")
    root.configure(bg="#f7f7f7")
    root.resizable(True, True)

    root.protocol("WM_DELETE_WINDOW", on_close)

    tk.Label(root, text="Chrome 路径:", bg="#f7f7f7").grid(row=0, column=0, sticky="w", padx=10, pady=5)
    chrome_path = tk.Entry(root, width=70)
    chrome_path.insert(0, CHROME_PATH)
    chrome_path.grid(row=0, column=1, padx=10, pady=5)
    tk.Button(root, text="选择", command=lambda: select_file(chrome_path, [("可执行文件", "*.exe")])).grid(row=0, column=2)

    tk.Label(root, text="SumatraPDF路径 (固定):", bg="#f7f7f7").grid(row=1, column=0, sticky="w", padx=10, pady=5)
    sumatra_label = tk.Label(root, text=SUMATRAPDF_PATH, bg="#f7f7f7", anchor="w", width=70)
    sumatra_label.grid(row=1, column=1, columnspan=2, sticky="w", padx=10, pady=5)

    tk.Label(root, text="临时文件存放目录:", bg="#f7f7f7").grid(row=2, column=0, sticky="w", padx=10, pady=5)
    temp_dir = tk.Entry(root, width=70)
    temp_dir.insert(0, TEMP_DIR)
    temp_dir.grid(row=2, column=1, padx=10, pady=5)
    tk.Button(root, text="选择", command=lambda: select_directory(temp_dir)).grid(row=2, column=2)

    tk.Label(root, text="监听地址:", bg="#f7f7f7").grid(row=3, column=0, sticky="w", padx=10, pady=5)
    host = tk.Entry(root, width=70)
    host.insert(0, FLASK_HOST)
    host.grid(row=3, column=1, padx=10, pady=5)

    tk.Label(root, text="运行端口:", bg="#f7f7f7").grid(row=4, column=0, sticky="w", padx=10, pady=5)
    port = tk.Entry(root, width=70)
    port.insert(0, FLASK_PORT)
    port.grid(row=4, column=1, padx=10, pady=5)

    tk.Button(root, text="保存并重启此程序", command=save_and_restart, bg="#007BFF", fg="white", padx=10, pady=5).grid(row=5, column=0, columnspan=3, pady=20)

    root.mainloop()

def select_file(entry_widget, filetypes):
    file_path = filedialog.askopenfilename(filetypes=filetypes)
    if file_path:
        entry_widget.delete(0, tk.END)
        entry_widget.insert(0, file_path)


def select_directory(entry_widget):
    dir_path = filedialog.askdirectory()
    if dir_path:
        entry_widget.delete(0, tk.END)
        entry_widget.insert(0, dir_path)

def setup_tray_icon():
    def open_service():
        url_host = "localhost" if FLASK_HOST == "0.0.0.0" else FLASK_HOST
        os.system(f"start http://{url_host}:{FLASK_PORT}/status")

    def open_config_thread():
        config_thread = Thread(target=open_config_window, args=(None,), daemon=True)
        config_thread.start()

    menu = Menu(
        MenuItem("配置参数", lambda: open_config_thread()),
        MenuItem("查看服务", lambda: open_service()),
        MenuItem("测试打印", lambda: os.system(f"start {os.path.join(BASE_DIR, 'print_test.html')}")),
        MenuItem("退出", lambda icon: icon.stop())
    )
    icon = Icon("clgl打印程序", Image.open(os.path.join(BASE_DIR, "icon.png")), "clgl打印程序", menu)
    icon.run()

def start_flask():
    serve(app, host=FLASK_HOST, port=FLASK_PORT)

def acquire_mutex():
    try:
        global mutex
        mutex = open(MUTEX_FILE, "w")
        msvcrt.locking(mutex.fileno(), msvcrt.LK_NBLCK, 1)
    except OSError:
        print("另一个实例正在运行，程序退出。")
        sys.exit(1)

if __name__ == '__main__':
    acquire_mutex()

    if not os.path.exists(TEMP_DIR):
        os.makedirs(TEMP_DIR)

    flask_thread = Thread(target=start_flask, daemon=True)
    flask_thread.start()
    setup_tray_icon()
